<?php
/**
 * Nephtali Web Framework
 * 
 * Written out of necessity, and crafted with love.
 *
 * @author Adam Richardson of Envision Internet Consulting, LLC
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */

/**
 * All functions and constants utilized by the Nephtali Framework.
 */
namespace n
{
    /**
     * Value accessor (values are immutable.)
     *
     * @staticvar array $values
     * @param string $name
     * @param mixed $value
     * @return mixed
     */
    function val($name, $value = null)
    {
        static $values = array();

        if (is_null($value))
        {
            return $values[$name];
        }
        else
        {
            if (isset($values[$name])) throw new \Exception('The value "' . $value . '" has already been set and cannot be changed.');
            
            $values[$name] = $value;
            return;
        }
    }
    /**
     * Adds variable value as a watched variable for debugging purposes.
     *
     * @param string name
     * @param mixed $value
     * @return void
     */
    function watch($name, $value)
    {
        if (!isset($_SESSION)) session_start();

        if(!isset($_SESSION['nephtali_debug'])) $_SESSION['nephtali_debug'] = array();

        // add the watched variable to the most recent nephtali_debug request
        $requests = $_SESSION['nephtali_debug'];

        $values = array_pop($requests);

        $values['vars'][] = array('name' => $name, 'value' => $value);

        $requests[] = $values;

        $_SESSION['nephtali_debug'] = $requests;
    }
}
/**
 * All Nephtali constants.
 */
namespace n\constant
{
    /**
     * Display errors in the output.
     */
    const CONF_ERRORS_DISPLAY = 9;
    /**
     * Suppress error output to the screen and log the error results.
     */
    const CONF_ERRORS_SUPPRESS_AND_LOG = 10;
    /**
     * Suppress error output to the screen.
     */
    const CONF_ERRORS_SUPPRESS = 11;
    /**
     * Debug capabilities turned off.
     */
    const CONF_DEBUG_OFF = 14;
    /**
     * Debug data appended to the display of the current page.
     */
    const CONF_DEBUG_APPEND_INFO = 15;
    /**
     * Any post requests that don't include file uploads are automatically redirected to avoid back-button issues.
     */
    const CONF_POST_AUTO_REDIRECT_WO_FILES = 16;
    /**
     * All post requests are automatically redirected to avoid back-button issues.
     */
    const CONF_POST_AUTO_REDIRECT_ALL = 17;
    /**
     * Automatic post redirects are turned off.
     */
    const CONF_POST_AUTO_REDIRECT_OFF = 18;
    /**
     * Placeholder databinding (e.g., {placeholder})
     */
    const DATABINDING_PLACEHOLDER = 30;
    /**
     * Form field databinding
     */
    const DATABINDING_FORM_FIELD = 31;
    /**
     * Convert date to MySQL datetime (same formate as MySQL timestamp)
     */
    const DATE_MYSQL_DATETIME = 'Y-m-d H:i:s';
    /**
     * Convert date to MySQL date
     */
    const DATE_MYSQL_DATE = 'Y-m-d';
    /**
     * Allow integers.
     *
     * Examples include:
     * 1
     * 65000
     */
    const FILTER_INT = 1;
    /**
     * Allow positive or negative numbers with decimal points.
     *
     * Examples include:
     * 2.5
     * 890.009
     * -23.9
     */
    const FILTER_DOUBLE = 2;
    /**
     * Allow alphanumeric text.
     *
     * Examples include:
     * abc678
     * 55uugr
     */
    const FILTER_ALPHANUM = 3;
    /**
     * Allow characters typically used in variable names.
     *
     * Examples include:
     * frank_the_terminator
     * symbol78
     */
    const FILTER_VAR_NAME = 19;
    /**
     * Allow valid email addresses.
     */
    const FILTER_EMAIL = 4;
    /**
     * Allow valid URL's.
     *
     * Examples include:
     * http://test.com
     * http://www.test.com
     * http://subdomain.test.com/index.php?var=value&var2=yes
     */
    const FILTER_URL = 5;
    /**
     * Allow valid US dates of the format m/d/yyyy.
     *
     * Examples include:
     * 9/7/2008
     * 09/09/2009
     */
    const FILTER_US_DATE = 22;
    /**
     * Allow valid US zip codes.
     *
     * Examples include:
     * 55555
     * 55555-5555
     */
    const FILTER_US_ZIP = 10;
    /**
     * Allow valid US phone numbers.
     *
     * Examples include:
     * 5555555555
     * 1-555-555-5555
     * 1(555)555-5555
     */
    const FILTER_US_PHONE = 11;
    /**
     * Allow a single line of text except for XML markup and danerous hackemes.
     */
    const FILTER_TEXT = 7;
    /**
     * Allow multiple lines of text except for XML markup and dangerous hackemes.
     */
    const FILTER_TEXT_MULTILINE = 9;
    /**
     * Allow all types of text except for dangerous hackemes.
     */
    const FILTER_XML = 6;
    /**
     * Allow everything under the sun.
     */
    const FILTER_ALL = 8;
    /**
     * Allow data that validates with custom regex (you must supply the regex in the opts array for the port.)
     */
    const FILTER_CUSTOM = 21;
    /**
     * Regex used for validating positive integers.
     */
    const REGEX_INT = '/^\d+$/';
    /**
     * Regex used for validating double numbers.
     */
    const REGEX_DOUBLE = '/^-?\d+(\.\d+)?$/';
    /**
     * Regex used for validating alphanumeric data (a - z, 0 - 9, case insensitive.)
     */
    const REGEX_ALPHANUM = '/^[a-zA-Z0-9]+$/';
    /**
     * Regex used for validating variable names (a - z, 0 - 9, '_', '-', case insensitive.)
     */
    const REGEX_VAR_NAME = '/^[-a-zA-Z0-9_]+$/';
    /**
     * Regex used for validating US phone numbers.
     */
    const REGEX_US_PHONE = '/^((1)?[2-9]\d{9})|(((1)?\([2-9]\d{2}\))|((1-)?[2-9]\d{2}-))\d{3}-\d{4}$/';
    /**
     * Regex used for validating US Zip codes.
     */
    const REGEX_US_ZIP = '/^\d{5}((-)?\d{4})?$/';
    /**
     * Regex used for validating basic text, free of HTML special characters.
     */
    const REGEX_TEXT = '/^[ \/a-zA-Z0-9.!@#$%&*:;?()_+=\'",\[\]-]+$/';
    /**
     * Regex used for validating basic multiline text, free of HTML special characters.
     */
    const REGEX_TEXT_MULTILINE = '/^[ \/a-zA-Z0-9.!@#$%&*:;?()_+=\'"\n\r,\[\]-]+$/';
    /**
     * Regex used for validating text that contains XML.
     */
    const REGEX_XML = '/^(.|\n)+$/';
    /**
     * Regex used for file names, which allows letters, numbers, whitespace, '.', and the underscore.
     */
    const REGEX_FILE_NAME = '/^[-a-zA-Z0-9_\s\.]+$/';
    /**
     * Regex used for URL's.
     */
    const REGEX_URL = '/^(http|https):\/\/(([-a-zA-Z0-9]+)\.)*([-a-zA-Z0-9]+)\.([a-z]{2,6})(\/([-a-zA-Z0-9#_.=?+&;:%])+)*$/';
    /**
     * Regex used for email addresses.
     */
    const REGEX_EMAIL = '/^[a-z0-9!#$%&*+=?^_`{|}~-]+(?:\.[a-z0-9!#$%&*+=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+([a-z]{2,6})$/';
    /**
     * Request for standard page output.
     */
    const REQUEST_STANDARD = 19;
    /**
     * Request for the output of one particular pipe.
     */
    const REQUEST_HTMLFRAG = 20;
    /**
     * Whitelist regex used to allow links through automatic encoding.
     */
    const WHITELIST_A_TAG = '/<a href="(((http|https):\/\/)|(mailto:[-a-zA-Z0-9#_.+]*@))(([-a-zA-Z0-9]+)\.)*([-a-zA-Z0-9]+)\.([a-z]{2,6})(\/([-a-zA-Z0-9#_.=?+&;:%])+)*">[ a-zA-Z0-9,)(\.\?!@-]+<\/a>/';
    /**
     * Whitelist regext that allows everything to be passed through the databinding process without automatic encoding.
     */
    const WHITELIST_ALLOW_ALL = 1024;
}
/**
 * Configuration file value functions.
 */
namespace n\config{
    /**
     * Provides access to stored config values.
     *
     * @param mixed $names If an array of names, returns an array of config values.  If one name, returns that one value.
     * @return mixed
     */
    function get($names)
    {
        static $values = 'empty';

        if (is_array($names) && $values == 'empty')
        {
            $values = $names;
            return;
        }
        else
        {
            // if array, return array
            if (is_array($names)) return array_intersect_key($values, array_fill_keys($names, 0));
            // if one value, return one value
            return $values[$names];
        }
    }
    /**
     * Primes config() function so subsequent calls to get() retrieve the value associated with the argument key.
     *
     * @param array $mode
     * @param array $dev
     * @param array $test
     * @param array $prod
     * @return void
     */
    function save($mode, array $dev, array $test, array $prod)
    {
        $defaults = array(
            'mode'                      => 'dev',
            'error_handling'            => \n\constant\CONF_ERRORS_SUPPRESS,
            'error_page'                => 'error.html',
            'encryption_key'            => null,
            'encryption_salt'           => null,
            'upload_directory'          => '',
            'upload_ftp_url'            => null,
            'sql_action_dsn'            => null,
            'sql_action_username'       => null,
            'sql_action_password'       => null,
            'sql_datasource_dsn'        => null,
            'sql_datasource_username'   => null,
            'sql_datasource_password'   => null,
            'post_request_handling'     => \n\constant\CONF_POST_AUTO_REDIRECT_WO_FILES
        );

        if ($mode == 'dev')
        {
            $s = $dev;
            $s['mode'] = 'dev';
        }
        elseif ($mode == 'test')
        {
            $s = $test;
            $s['mode'] = 'test';
        }
        elseif ($mode == 'prod')
        {
            $s = $prod;
            $s['mode'] = 'prod';
        }
        else
        {
            throw new \Exception('The mode must be one of the following strings: dev, test, prod.');
        }

    	get(array_merge($defaults, $s));
    }
}
/**
 * Function wrappers for CURL operations such as getting contents of files, uploading via FTP, etc.
 */
namespace n\curl
{
    /**
     * FTP's a file to a server via curl.
     *
     * @param string $local_file_path
     * @param string $file_name
     * @param string $ftp_domain
     * @param string $ftp_username
     * @param string $ftp_password
     */
    function ftp_upload($local_file_path, $local_file_size, $file_name, $ftp_domain, $ftp_username, $ftp_password)
    {
        $fp = fopen($local_file_path, 'r');
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, 'ftp://'.urlencode($ftp_username).':'.urlencode($ftp_password).'@'.$ftp_domain.$file_name);
        curl_setopt($ch, CURLOPT_UPLOAD, 1);
        curl_setopt($ch, CURLOPT_INFILE, $fp);
        curl_setopt($ch, CURLOPT_INFILESIZE, $local_file_size);
        // needed so output isn't echo'd back
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_exec($ch);
        $error_no = curl_errno($ch);
        curl_close ($ch);
        if ($error_no != 0) {
            if (is_int($error_no))
            {
                throw new \Exception('There was an error uploading the file \''.$file_name.'\'. CURL error number:'.$error_no);
            }
            else
            {
                throw new \Exception('There was an error uploading the file, and the CURL error number was invalid, too.');
            }
        }
    }
    /**
     * Save a file upload using CURL to ftp to the site and upload the file.
     * 
     * This is useful on shared hosts where one does not have permissions to save files with PHP.  
     * In this case, you can set up an FTP account to an uploads directory, and then use this function
     * to upload the file to the directory.
     *
     * @param <type> $post_key
     * @param <type> $file_name
     * @param <type> $ftp_domain
     * @param <type> $ftp_username
     * @param <type> $ftp_password
     */
    function save_upload($post_key, $file_name, $ftp_domain, $ftp_username, $ftp_password)
    {
        ftp_upload($local_file_path = $_FILES[$post_key]['tmp_name'], $local_file_size = $_FILES[$post_key]['size'], $file_name, $ftp_domain, $ftp_username, $ftp_password);
    }
    /**
     * Retrieves the contents of a url via CURL.
     *
     * Unlike file_get_contents(), this function is useful because you can set a timeout on the request,
     * which is useful in situations where you don't want an entire page to error out just because of one
     * section of the page.
     *
     * @param string $url
     * @param array $post_data
     * @param int $connect_timeout
     * @param int $transaction_timeout
     * @return string
     */
    function get_contents($url, array $post_data = array(), $connect_timeout = 1, $transaction_timeout = 2)
    {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
        curl_setopt($ch, CURLOPT_TIMEOUT, $transaction_timeout);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

        if ($post_data)
        {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post_array);
        }

        $str = curl_exec($ch);
        $error_no = curl_errno($ch);
        curl_close ($ch);
        
        if($error_no != 0)
        {
            if(is_int($error_no))
            {
                throw new \Exception('There was an error retreiving the string contents of the url \''.$url.'\'. CURL error number:'.$error_no);
            }
            else
            {
                throw new \Exception('There was an error retreiving the url \''.$url.'\', and the CURL error number was invalid, too.');
            }
        }
        else
        {
            return $str;
        }
    }
    /**
     * Retrieves the contents of a multiple urls in parallel via CURL.
     *
     * In situations where some tasks take a significant amount of time relative to others, processing the tasks in parallel can buy you an overall
     * improvement in performance, as the total time now equals the time for the longest task as opposed to the total time for each of the indivual tasks.
     *
     * @param array $requests An array of requests, with each request containing an associative array with the values url (required string), post_data (array), connect_timeout (int), transaction_timeout (int).
     * @return array
     */
    function get_contents_parallel(array $requests)
    {
        $chs = array();
        $responses = array();
        $mh = curl_multi_init();
        $running = null;

        foreach ($requests as $request)
        {
            $chs[$request['url']] = curl_init();
            curl_setopt($chs[$request['url']], CURLOPT_URL, $request['url']);
            curl_setopt($chs[$request['url']], CURLOPT_CONNECTTIMEOUT, $request['connect_timeout']);
            curl_setopt($chs[$request['url']], CURLOPT_TIMEOUT, $request['transaction_timeout']);
            curl_setopt($chs[$request['url']], CURLOPT_RETURNTRANSFER, 1);

            if ($request['post_data'])
            {
                curl_setopt($chs[$request['url']], CURLOPT_POST, 1);
                curl_setopt($chs[$request['url']], CURLOPT_POSTFIELDS, $request['post_array']);
            }

            curl_multi_add_handle($mh, $chs[$request['url']]);
        }

        do {
            curl_multi_exec($mh, $running);
        } while($running > 0);

        foreach($chs as $url => $ch)
        {
            $responses[$url] = curl_multi_getcontent($ch);
            curl_multi_remove_handle($mh, $ch);
        }

        curl_multi_close($mh);

        return $responses;
    }
}
namespace n\curl\request
{
    /**
     * Registers a web request for parallel processing using CURL.
     *
     * @param string $url
     * @param array $post_data
     * @param int $connect_timeout
     * @param int $transaction_timeout
     */
    function register($url, array $post_data = array(), $connect_timeout = 1, $transaction_timeout = 2)
    {
        \n\z\value($domain = 'request', $command = 'set', $inputs = array('url' => $url, 'post_data' => $post_data, 'connect_timeout' => $connect_timeout, 'transaction_timeout' => $transaction_timeout), $index = 'url');
    }
}
namespace n\curl\response
{
    /**
     * Retrieves the response from the corresponding web request.
     *
     * @param string $url
     * @return string
     */
    function get($url)
    {
        $url_is_set = \n\z\value($domain = 'response', $command = 'isset', $inputs = array($url));
        if ($url_is_set[$url])
        {
            $responses = \n\z\value($domain = 'response', $command = 'get', $inputs = array($url));
            return $responses[$url]['output'];
        }

        $req_settings = current(\n\z\value($domain = 'request', $command = 'get', $inputs = array($url)));

        return \n\curl\get_contents($url = $req_settings['url'], $post_data = $req_settings['post_data'], $connect_timeout = $req_settings['connect_timeout'], $transaction_timeout = $req_settings['transaction_timeout']);
    }
}
/**
 * Bing web services functions.
 */
namespace n\ws\bing
{
    /**
     * Retrieve the search results (title, url, description) for a Bing-based site query through the API.
     *
     * @param string $site
     * @param string $app_id
     * @param string $query
     * @param int $limit
     * @return array
     */
    function search_site($site, $app_id, $query, $limit = 20)
    {
        $query = str_replace(' ', '+', $query);

        $url = "http://api.search.live.net/json.aspx?Appid=".urlencode($app_id)."&query=site:".urlencode($site)."+".urlencode($query)."&sources=web&web.count=".urlencode($limit);

        $json_results = json_decode(n\curl\get_contents($url), true);

        $results = $json_results['SearchResponse']['Web']['Results'];

        for ($i = 0; $i < count($results); $i++)
        {
            $results[$i] = array_change_key_case($results[$i]);
        }

        return $results;
    }
}
/**
 * Yahoo! web services functions.
 */
namespace n\ws\yahoo
{
    /**
     * Retrieve the search results (title, url, clickurl, abstract, date, size) for a Yahoo! Boss-based site query through the API.
     *
     * @param string $site
     * @param string $app_id
     * @param string $query
     * @param int $limit
     * @return array
     */
    function search_site($site, $app_id, $query, $limit = 20)
    {
        $url = 'http://boss.yahooapis.com/ysearch/web/v1/'.urlencode($query).'?appid='.urlencode($app_id).'&sites='.urlencode($site).'&count='.urlencode($limit);

        $json_results = json_decode(\n\curl\get_contents($url), true);

        return $json_results['ysearchresponse']['resultset_web'];
    }
}
/**
 * Page markup functions.
 */
namespace n\markup
{
    /**
     * Returns the array of sections formed by a pipe, view, or data region in the markup.
     *
     * @param string $markup
     * @param string $name
     * @param string $type
     * @return array
     */
    function get_sections($markup, $type, $name = null)
    {
        if ($type != 'data' && count($sections = explode('<!--'.$type.':'.$name.'-->', $markup)) == 3)
        {
            // check for error views, the one required view.
            if (($type == 'pipe') && (count(explode('<!--view:error-->', $sections[1])) != 3))
                throw new \Exception("The pipe $name does not have the required error view");
        
            return $sections;
        }
        else
        {
            if ($type == 'data')
            {
                if (count($sections = explode('<!--data-->', $markup)) == 3)
                {
                    return $sections;
                }
                else
                {
                    return array($markup);
                }
            }
            else
            {
                throw new \Exception('The following '.$type.' could not be properly found in the markup: '.$name);
            }
        }
    }
}
/**
 * Pipe functions.
 *
 * In Nephtali, Pipes are responsible for rendering their respective regions in a page,
 * and controlling the general flow of an application (login mechanisms are handled by pipes
 * without output.)
 */
namespace n\pipe
{
    /**
     * Retrieve the settings for registered pipes.  With no argument, all pipes are retrieved.
     *
     * @param mixed $names If an array of names is provided, an array of pipes is returned.  If one name is provided (string), then that one pipe is returned.  Default is an empty array, which leads to returning all registered pipes.
     * @return mixed
     */
    function get($names = array())
    {
        if (is_array($names)) return \n\z\value($domain ='pipe', $command = 'get', $inputs = $names);

        return current(\n\z\value($domain = 'pipe', $command = 'get', $inputs = array($names)));
    }
    /**
     * Register a pipe for the page.
     *
     * @param string $name Name of the pipe.
     * @param mixed $function Rendering function (either string name or a reference to the function, or an anonymous function.)
     * @param array $opts Array of associative arrays containing optional settings.
     * @return void
     */
    function register($name, $function, array $opts = array())
    {
        // merge with defaults
        $opts = array_merge(array('required_pipes' => array(), 'has_output' => true, 'auto_error_handling' => true), $opts);
        return \n\z\value($domain = 'pipe', $command = 'set', $inputs = array('name' => $name, 'function' => $function, 'opts' => $opts));
    }
    /**
     * Register a pipe that conditionally runs any one of several functions depending upon the ports present on a particular request.
     *
     * @param string $name Name of the pipe.
     * @param array $actions Associative array with keys formed by calls to n\port\signature() (either 0 or a unique index) and values representing the function to run if the signature is valid.  Only the first valid signature is run, so order is important.
     * @param array $opts Associative array of options.
     * @return void
     */
    function register_action($name, array $actions, array $opts = array())
    {
        foreach ($actions as $signature => $function)
        {
            if ($signature)
            {
                return register($name, $function, $opts);
            }
        }

        register($name, function($markup){return '';}, $opts = array());
    }
}
/**
 * Port functions
 *
 * In Nephtali, ports are responsible for validing Get, Post, Cookie, and File inputs.
 */
namespace n\port
{
    /**
     * Retrieve an array of the port settings for the ports named in the array.  If no second argument is provided, by default the value of the port is returned.
     *
     * @param mixed $names If provided an array of names, an array of port values is returned.  If provided one name, one port value is returned.
     * @param string $key Key of the port array element to retrieve (defaults to 'value'.)
     * @return array
     */
    function get($names = array(), $key = 'value')
    {
        if (is_array($names))
        {
            $ports = \n\z\value($domain = 'port', $command = 'get', $inputs = $names);

            // remove internal ports if it's a request for all ports
            // this should eventually be removed by refactoring so these ports are validated without registering
            if (!count($names))
            {
                unset($ports['nports']);
                unset($ports['npipe']);
                unset($ports['nmode']);
            }

            foreach ($ports as $name => $port)
            {
                if (!$port['valid']) throw new \Exception('You cannot retrieve the value of an invalid port with n\port\get().  Check to make sure you are properly checking port validity of your port with the function n\port\validate().');
                $ports[$name] = $port[$key];
            }

            return $ports;
        }
        else
        {
            $port = current(\n\z\value($domain = 'port', $command = 'get', $inputs = array($names)));

            if (!$port['valid']) throw new \Exception('You cannot retrieve the value of an invalid port with n\port\get().  Check to make sure you are properly checking port validity of your port with the function n\port\validate().');

            return $port[$key];
        }
    }
    /**
     * Register a port for use within pipe functions.
     *
     * @param string $name
     * @param mixed $value
     * @param array $opts Options include min_length, max_length, filter, regex, allow_null, allow_array, encrypted, error_messages, formatted_name.
     * @return void
     */
    function register($name, $value, array $opts = array())
    {
        return \n\z\port\save(array('name' => $name, 'value' => $value, 'opts' => $opts, 'type' => 'char_data'));
    }
    /**
     * Register a file port for use wihtin pipe functions.
     *
     * @param string $name
     * @param mixed $value
     * @param array $opts
     * @return void
     */
    function register_file($name, $value, array $opts = array())
    {
        return \n\z\port\save(array('name' => $name, 'value' => $value, 'opts' => $opts, 'type' => 'file'));
    }
    /**
     * Determine if the array of ports are set on the given page request.
     *
     * This is useful if you only want to insert an element if there's no id, or update an element if there is, etc.
     *
     * @param array $ports Array of port names to check.
     * @return int Reterns 0 if the signature is invalid, or a unique index of the number of times n\port\signature has been called (useful for actions pipe.)
     */
    function signature(array $ports)
    {
        static $counter = 1;

        foreach (\n\z\value($domain = 'port', $command = 'get', $inputs = $ports) as $port)
        {
            if (!$port['signature']) return 0;
        }

        return $counter++;
    }
    /**
     * Validate the group of ports named in the array.
     *
     * @param array $ports
     * @return array $rows Array of associative arrays contained containing port names and error messages (array is empty if all ports were valid.)
     */
    function validate(array $ports = array())
    {
        $rows = array();
        $retrieved_ports = \n\z\value($domain = 'port', $command = 'get', $inputs = $ports);

        // remove internal ports if it's a request for all ports
        // this should eventually be refactored by validating these ports without registering
        if (!count($ports))
        {
            unset($retrieved_ports['nports']);
            unset($retrieved_ports['npipe']);
            unset($retrieved_ports['nmode']);
        }

        foreach ($retrieved_ports as $name => $port)
        {
            if (!is_null($port['error_message']))
                $rows[$port['name']] = array('message' => $port['error_message']);
        }

        return $rows;
    }
}
namespace n\row
{
    /**
     * Databinds associative array row to the markup according to the mappings.
     *
     * @param string $markup
     * @param array $row
     * @param array $opts
     * @return string
     */
    function databind($markup, $row, $opts)
    {
        foreach ($row as $key => $value)
        {
            if (isset($opts['whitelists'][$key]) && (count($opts['whitelists'][$key])) && $opts['whitelists'][$key][0] == \n\constant\WHITELIST_ALLOW_ALL)
            {
                $markup = str_replace('{'.$key.'}', $value, $markup);
            }
            else
            {
                $markup = str_replace('{'.$key.'}', \n\str\encode($value, $opts['whitelists'][$key]), $markup);
            }
        }

        return $markup;
    }
    /**
     * Databinds associative array row to the markup matching array keys to form name attributes.
     * 
     * @param string $markup
     * @param array $row
     * @param array $opts
     * @return string 
     */
    function databind_form($markup, $row, $opts)
    {
        foreach($row as $key => $value)
        {
            $matches = array();

            if(preg_match_all(
                $regex = "/<input[^>]*((name=\"$key\")|(name='$key'))[^>]*>/s",
                $markup,
                $matches
            ))
            {
                // matches[0] is array
                $types = array();
                if (preg_match('/type=(\'|")([a-zA-Z0-9_-]+)(\'|")/', $matches[0][0], $types))
                {
                    $type = $types[2];

                    switch ($type)
                    {
                        case 'text':
                            // make sure it didn't already have a value
                            $new_element = preg_replace('/value=(\'|")([%&a-zA-Z0-9_;-]+)(\'|")/', '', $matches[0][0]);
                            // remember to escape output
                            $new_element = str_replace(" type=", " value=\"".\n\str\encode($value)."\" type=", $new_element);
                            $markup = str_replace($matches[0][0], $new_element, $markup);
                            break;
                        case 'radio':
                            $search = 'value="'.$value.'"';
                            foreach ($matches[0] as $elem)
                            {
                                if (strpos($elem, $search) !== false)
                                {
                                    // make sure it didn't already have a select
                                    $new_element = preg_replace('/checked=(\'|")checked(\'|")/', '', $elem);
                                    $new_element = str_replace(" type=", " checked=\"checked\" type=", $new_element);
                                    $markup = str_replace($elem, $new_element, $markup);
                                    break;
                                }
                            }
                            break;
                        case 'checkbox':
                            // only set if value is true
                            if ($value)
                            {
                                // make sure it didn't already have a select
                                $new_element = preg_replace('/checked=(\'|")checked(\'|")/', '', $matches[0][0]);
                                $new_element = str_replace(" type=", " checked=\"checked\" type=", $new_element);
                                $markup = str_replace($matches[0][0], $new_element, $markup);
                            }
                            break;
                        case 'hidden':
                            // make sure it didn't already have a value
                            $new_element = preg_replace('/value=(\'|")([%&a-zA-Z0-9_;-]+)(\'|")/', '', $matches[0][0]);
                            // remember to escape output
                            $new_element = str_replace(" type=", " value=\"".\n\str\encode($value)."\" type=", $new_element);
                            $markup = str_replace($matches[0][0], $new_element, $markup);
                            break;
                    }
                }
                continue;
            }

            if (preg_match(
                $regex = "/(<textarea[^>]*((name=\"$key\")|(name='$key'))[^>]*>)(.*?)<\/textarea>/s",
                $markup,
                $matches
            ))
            {
                // matches[0] is string
                $new_element = $matches[1].\n\str\encode($value).'</textarea>';
                $markup = str_replace($matches[0], $new_element, $markup);
                continue;
            }

            if (preg_match(
                $regex = "/<select[^>]*((name=\"$key\")|(name='$key'))[^>]*>(.*?)<\/select>/s",
                $markup,
                $matches
            ))
            {
                // matches[0] is string
                // make sure it didn't already have a select
                $new_element = preg_replace('/selected=(\'|")selected(\'|")/', '', $matches[0]);
                $new_element = str_replace("value=\"$value\"", " selected=\"selected\" value=\"$value\"", $new_element);
                $markup = str_replace($matches[0], $new_element, $markup);
                continue;
            }
        }

        return $markup;
    }
    /**
     * Formats the date fields contained within a row (e.g., a key => value associative array) to and from the formatting options found at http://www.php.net/manual/en/function.date.php.
     *
     * @param array $rows Array of associative array rows that contains the date fields to be formatted.
     * @param array $date_fields Associative array containing field names as keys and the corresponding formatting string (some predefined constants are available under n\constant\DATE_XXX) for the php date() function.  Additionally, PHP also has predefined consants available: http://www.php.net/manual/en/class.datetime.php#datetime.constants.types
     * @return array
     */
    function format_dates(array $row, array $date_fields)
    {
        foreach ($date_fields as $key => $value)
        {
            $row[$key] = date($value, strtotime($row[$key]));
        }

        return $row;
    }
}
/**
 * Security functions, including encryption, decryption.
 */
namespace n\security
{
    /**
     * Encrypts value using Rijndael 256.
     *
     * @param string|int $value
     * @param string|int $key
     * @param string|int $salt
     * @return string
     */
    function encrypt($value, $key = null, $salt = null)
    {
        if (is_null($key))
            $key = \n\config\get('encryption_key');
            
        if (is_null($salt))
            $salt = \n\config\get('encryption_salt');
        
        $join = function($value1,$value2)
        {
            return base64_encode($value1).'|'.base64_encode($value2);
        };

        $hash = function($value,$salt) use ($key)
        {
            return hash_hmac('sha256', $value, $key, true);
        };
    
        $hashedKey = $hash($key, $salt);
    
        // time serves as nonce
        $mt = microtime(true);
    
        // concat base64'd mt and value
        $dataAndMessage = $join($mt, $value);
    
        // get mac for data and message
        $mac = $hash($dataAndMessage, $hashedKey);
    
        $dataAndMessageAndMac = $join($dataAndMessage, $mac);
    
        $cipherText = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $hashedKey, $dataAndMessageAndMac, 'cbc', $hash($mt, $salt));
    
        return $join($mt, $cipherText);
    }
    /**
     * Decrypts strings encrypted with the encrypt() function (which uses Rijndael 256.)
     *
     * @param string $value
     * @param string|int $key
     * @param string|int $salt
     * @return string|int
     */
    function decrypt($value, $key = null, $salt = null)
    {
        if (is_null($key))
            $key = \n\config\get('encryption_key');
            
        if (is_null($salt))
            $salt = \n\config\get('encryption_salt');
        
        $break = function($string)
        {
            $elements = explode('|', $string);
            $elements[0] = base64_decode($elements[0]);
            $elements[1] = base64_decode($elements[1]);
            return $elements;
        };
    
        $hash = function($value,$salt) use ($key)
        {
            return hash_hmac('sha256', $value, $key, true);
        };
    
        // sha256_d converts key to 128 bit result in binary
        $hashedKey = $hash($key, $salt);
    
        list($mt, $cipherText) = $break($value);
    
        $dataAndMessageAndMac = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $hashedKey, $cipherText, 'cbc', $hash($mt, $salt));
    
        list($dataAndMessage, $mac) = $break($dataAndMessageAndMac);
    
        $hmacResult = $hash($dataAndMessage, $hashedKey);
    
        if($hmacResult != trim($mac))
        {
            throw new \Exception('The encrypted value appears to be corrupted.');
        }
        else
        {
            list($mt, $message) = $break($dataAndMessage);
            return $message;
        }
    }
}
/**
 * SQL select functions.
 */
namespace n\sql\source
{
    /**
     * Creates and returns a connection handle for data sources (i.e., queries that read rows of a table, such as select queries.)
     *
     * If no arguments are supplied, the sql_source values in nconfig are used to create the connection.
     *
     * @param $dsn
     * @param $username
     * @param $password
     * @return unknown_type
     */
    function connect($dsn = null, $username = null, $password = null)
    {
        if (is_null($dsn))
            $dsn = \n\config\get('sql_source_dsn');
    
        if (is_null($username))
            $username = \n\config\get('sql_source_username');
    
        if (is_null($password))
            $password = \n\config\get('sql_source_password');
    
        $dbh = new \PDO($dsn, $username, $password);
        $dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
    
        return $dbh;
    }

    /**
     * Performs read-only ('SELECT') queries on tables and returns results.
     *
     * @param array $query
     * @param $inputs
     * @param $dbh
     * @return array
     */
    function query($query, array $inputs = array(), $dbh = null)
    {
        if (is_null($dbh))
            $dbh = connect();
    
        $sth = $dbh->prepare($query);
    
        if (!$sth->execute($inputs))
        {
            throw new \Exception('The source query could not be completed.');
        }
    
        return $sth->fetchAll(\PDO::FETCH_ASSOC);
    }
}
/**
 * SQL insert, updaste, and delete functions.
 */
namespace n\sql\action
{
    /**
     * Creates and returns a connection handle for data actions (i.e., queries that alter rows of the table, such as insert, update, and delete.)
     *
     * If no arguments are supplied, the sql_action values in nconfig are used to create the connection.
     *
     * @param $dsn
     * @param $username
     * @param $password
     * @return unknown_type
     */
    function connect($dsn = null, $username = null, $password = null)
    {
        if (is_null($dsn))
            $dsn = \n\config\get('sql_action_dsn');
    
        if (is_null($username))
            $username = \n\config\get('sql_action_username');
    
        if (is_null($password))
            $password = \n\config\get('sql_action_password');
    
        $dbh = new \PDO($dsn, $username, $password);
        $dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
    
        return $dbh;
    }
    /**
     * Performs queries that alter table rows, such as 'INSERT', 'UPDATE', and 'DELETE' queries.
     *
     * @param array $query
     * @param $inputs
     * @param $dbh
     * @return unknown_type
     */
    function query($query, array $inputs, $dbh = null)
    {
        if (is_null($dbh))
            $dbh = connect();
    
        $sth = $dbh->prepare($query);
    
        if (!$sth->execute($inputs))
        {
            throw new \Exception('The action query could not be completed.');
        }
    
        if(strtolower(substr(trim($query), 0, 6)) == 'insert')
            return $dbh->lastInsertId();
    
        return;
    }
    /**
     * Convenience function for deleting a row from a table by it's id (must follow the convention that the table primary key is named 'id'.)
     *
     * @param $table_name
     * @param $id
     * @param $dbh
     * @return unknown_type
     */
    function delete($table_name, $id, $dbh = null)
    {
        return query("DELETE FROM $table_name WHERE id = ?", array($id), $dbh);
    }
    /**
     * Convenience function for inserting an associative array row into a table (array keys must match the column names of the table.)
     *
     * @param array $table_name
     * @param $inputs
     * @param $dbh
     * @return unknown_type
     */
    function insert($table_name, array $inputs, $dbh = null)
    {
        $keys = array_keys($inputs);
        $fields = implode(',', $keys);
        $place_holders = implode(',', array_map(function($v){return ':'.$v;}, $keys));
        return query("INSERT INTO $table_name ($fields) VALUES ($place_holders)", $inputs, $dbh);
    }
    /**
     * Convenience function for updating a table row with values from an associative array (array keys must match the column names of the table.)
     *
     * @param array $table_name
     * @param $inputs
     * @param $dbh
     * @return unknown_type
     */
    function update($table_name, array $inputs, $dbh = null)
    {
        if (!isset($inputs['id']))
            throw new \Exception('The action_update method requires that the table your updating follow the convention that it has a primary key called "id".  No key was found that matched, so try using the action_query() function to perform your update.');

        $keys = array_keys($inputs);
        unset($keys[array_search('id', $keys)]);
        $update_values = implode(',', array_map(function($v){return $v.' = :'.$v;}, $keys));
        return query("UPDATE $table_name SET $update_values WHERE id = :id", $inputs, $dbh);
    }
}
/**
 * String manipulation functions.
 */
namespace n\str
{
    /**
     * Wrapper for PHP's str_replace that encodes the replaced values and provides the opportunity to allow string whitelists that shouldn't be encoded.
     *
     * @param string $search
     * @param string $replace
     * @param string $subject
     * @param array $whitelist
     * @return string
     */
    function replace($search, $replace, $subject, $whitelist = array(), $charset = 'UTF-8')
    {
        return str_replace($search, encode($replace, $whitelist), $subject);
    }
    /**
     * HTML-encodes output and allows whitelists of values that should be let through without encoding.
     *
     * @param string $string
     * @param array $allowedHTML
     * @return string
     */
    function encode($string, $allowed_html = array(), $charset = 'UTF-8')
    {
        // use htmlspecialchars because it only does the 5 most important chars, and doesn't mess them up
        $string = htmlspecialchars($string, ENT_QUOTES, $charset);
    
        if (count($allowed_html))
        {
            foreach($allowed_html as $tag)
            {
                if ((strpos($tag, '/') == 0) && (strrpos($tag, '/') == (strlen($tag)-1)))
                {
                    // Save entitied version of regex
                    // This should be acceptable because <, >, &, ', ", don't have special meaning in regexes
                    $entitied_regex = htmlspecialchars($tag, ENT_QUOTES, $charset);

                    // Tag represents a regular expression of the tag
                    $string = preg_replace_callback($entitied_regex, function($matches){return html_entity_decode($matches[0]);}, $string);
                }
                else
                {
                    // Save entitied version of tag, then look for occurances of it in string and replace them
                    $entitied_tag = htmlspecialchars($tag, ENT_QUOTES, $charset);
                    $string = str_replace($entitied_tag, $tag, $string);
                }
            }
        }
    
        return $string;
    }
}
/**
 * URL functions, such as redirects, parsing, etc.
 */
namespace n\url
{
    /**
     * Checks if the current url path includes any one of the path arguments.
     *
     * For example, if you wanted to check to see if the page was the admin directory of the site, the $paths argument would be $paths = array('/admin').
     *
     * @param array $paths
     * @return bool
     */
    function current_path(array $paths)
    {
        foreach ($paths as $path)
        {
            if (strpos($_SERVER['PHP_SELF'], $path) === 0)
            {
                return true;
            }
        }

        return false;
    }
    /**
     * Immediately redirects from the current page to the URL.
     *
     * @param string $url
     * @return void
     */
    function redirect($url)
    {
        ob_clean();
        header('Location: '.$url);
        exit();
    }
    /**
     * Retrieve the current url.
     *
     * @return string
     */
    function get_current()
    {
        if (isset($_SERVER['SCRIPT_URI']))
        {
            $url = $_SERVER['SCRIPT_URI'];

            if (count($_GET))
            {
                $url .= '?';
                foreach ($_GET as $key=>$value)
                {
                    $url .= $key.'='.$value.'&';
                }
            }

            return $url;
        }

        $url = '';
        $port = '';

        if (isset($_SERVER['HTTP_HOST']))
        {
            $colon = strpos($_SERVER['HTTP_HOST'], ':');

            if ($colon === false)
            {
                if (isset($_SERVER['SERVER_PORT']))
                {
                    $port = ':' . $_SERVER['SERVER_PORT'];
                }

                $url = $_SERVER['HTTP_HOST'];

            }
            else
            {
                $url = substr($_SERVER['HTTP_HOST'], 0, $colon);
                $port = substr($_SERVER['HTTP_HOST'], $colon);
            }
        }
        elseif(isset($_SERVER['SERVER_NAME']))
        {
            $url = $_SERVER['SERVER_NAME'];
            if (isset($_SERVER['SERVER_PORT']))
            {
                $port = ':' . $_SERVER['SERVER_PORT'];
            }
        }

        if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
        {
            $url = 'https://' . $url;
            if ($port == ':443')
            {
                $port = '';
            }
        }
        else
        {
            $url = 'http://' . $url;
            if ($port == ':80')
            {
                $port = '';
            }
        }

        $url .= $port;

        if (isset($_SERVER['REQUEST_URI']))
        {
            $url .= $_SERVER['REQUEST_URI'];
        }
        else
        {
            throw new \Exception('We could not retrieve the URL for this page, so you\'ll have to turn off post reloading in nconfig.');
        }

        if (count($_GET))
        {
            $url .= '?';
            foreach ($_GET as $key=>$value)
            {
                $url .= $key.'='.$value.'&';
            }
        }

        return $url;
    }
}
namespace n\view
{
    /**
     * Databinds rows of associative arrays to the markup contained within the <!--data--> region of the pipe markup.
     *
     * @param $markup
     * @param $rows
     * @param $opts
     * @return string
     */
    function databind($markup, $rows, array $opts = array())
    {
        if (count($regions = \n\markup\get_sections($markup, $type = 'data')) == 1)
        {
            return $markup;
        }

        $data_markup = $regions[1];

        $regions[1] = '';

        foreach ($rows as $row)
        {
            $row_markup = $data_markup;

            foreach ($opts['functions'] as $func)
            {
                $row_markup = $func($data_markup, $row);
            }

            if ($opts['databinding'] == \n\constant\DATABINDING_FORM_FIELD)
            {
                $regions[1] .= \n\row\databind_form($row_markup, $row, $opts);
            }
            elseif ($opts['databinding'] == \n\constant\DATABINDING_PLACEHOLDER)
            {
                $regions[1] .= \n\row\databind($row_markup, $row, $opts);
            }
            else
            {
                throw new \Exception('The "databinding" option was not recognized. View the available DATABINDING constants.');
            }
        }

        return implode('', $regions);
    }
    /**
     * Returns view markup after any required databinding.
     *
     * @param string $name
     * @param string $markup
     * @param array $rows
     * @param array $opts
     * @return string
     */
    function render($name, $markup, $rows = array(), array $opts = array())
    {
        $opts = array_merge(array('databinding' => \n\constant\DATABINDING_PLACEHOLDER), $opts);

        $markup_sections = \n\markup\get_sections($markup, $type = 'view', $name);

        if (count($markup_sections) != 3)
        {
            throw new \Exception("We could not find the markup for the view '$name'.  Please make sure you have view comments in place for each of the views your pipes expect.");
        }

        return databind($markup_sections[1], $rows, $opts);
    }
}
/**
 * Output functions.
 *
 * Mechanism in Nephtali for you to output data to regions outside standard pipes.
 * Values are immutable and automatically escaped.
 */
namespace n\output
{
    /**
     * Returns the array of immutable Nephtali outputs named in the arugment.
     *
     * @param array $names
     * @return array
     */
    function get(array $names)
    {
        return \n\z\value($domain = 'output', $command = 'get', $inputs = $names);
    }
    /**
     * Saves an output to be databound to page (values are immutable, so get them right the first time :)
     *
     * @param $name
     * @param $value
     * @return unknown_type
     */
    function register($name, $value)
    {
        \n\z\value($domain = 'output', $command = 'set', $inputs = array('name' => $name, 'value' => $value));
    }
}
/**
 * XML functions using SimpleXML to simplify tasks.
 */
namespace n\xml
{
    /**
     * Parse an XML string using functions specific to particular xpath node results.
     *
     * If there is a default namespace (e.g., xmlns="http://website.com"), you don't have to declare a default namespace and use it to reference each element as you normally would.
     *
     * @param string $xml_str
     * @param mixed $result String, array, object returned to the from the function after processing.
     * @param array $xpath_functions Associative array of xpath keys and function values.  Each function must accept a $node argument and a $result argument.
     * @param array $namespaces
     * @return mixed 
     */
    function parse($xml_str, $result, array $xpath_functions, array $namespaces = array())
    {
        // hack to avoid default namespace issues
        $xml = new \SimpleXMLElement(str_replace("xmlns=", "ns=", $xml_str));

        foreach ($namespaces as $key => $value)
        {
            $xml->registerXPathNamespace($key, $value);
        }

        foreach ($xpath_functions as $xpath => $function)
        {
            $matches = $xml->xpath($xpath);

            foreach ($matches as $node)
            {
                $result = $function($node, $result);
            }
        }

        return $result;
    }
}
namespace n\z\post
{
    function process_request()
    {
        if ((count($_POST) > 0) && (count($_FILES) < 1) && (\n\config\get('post_request_handling') == \n\constant\CONF_POST_AUTO_REDIRECT_WO_FILES))
        {
            save_vars_to_session();
            set_redirect_flag();
            \n\url\redirect($url= \n\url\get_current());
        }
        else if((count($_POST) > 0) && (count($_FILES) > 0) && (\n\config\get('post_request_handling') == \n\constant\AUTO_REDIRECT_ALL_POSTS))
        {
            /*
             * still have to add, don't do redirect for now
             *
             */
        }
        else if($_COOKIE['nephtali_post_redirect'] == 1)
        {
            if (!isset($_SESSION)) session_start();

            if (isset($_SESSION['nephtali_post_vars']))
            {
                create_vars_from_session();
            }

            unset_redirect_flag();
        }
    }
    function save_vars_to_session()
    {
        if (!isset($_SESSION)) session_start();
        
        foreach ($_POST as $key=>$value)
        {
            $_SESSION['nephtali_post_vars'][$key] = $value;
            unset($_POST[$key]);
        }
    }
    function create_vars_from_session()
    {
        if (!isset($_SESSION)) session_start();

        foreach ($_SESSION['nephtali_post_vars'] as $key=>$value)
        {
            $_POST[$key] = $value;
            unset($_SESSION['nephtali_post_vars'][$key]);
        }
    }
    function set_redirect_flag()
    {
        setCookie('nephtali_post_redirect', 1);
    }
    function unset_redirect_flag()
    {
        setCookie('nephtali_post_redirect', 0);
    }
}
namespace n\z\file
{
    /**
     * File exists function that takes into account include path.
     *
     * @param string $file
     * @return bool
     */
    function exists($file)
    {
        $paths = explode(PATH_SEPARATOR, get_include_path());
    
        foreach ($paths as $path)
        {
            $path = $path.'/'.$file;
    
            if (file_exists($path))
            {
                return true;
            }
        }
    
        return false;
    }
}
/**
 * All n\z files are internal.  Use at your own risk!!!
 */
namespace n\z
{
    /**
     * Reset Get, Post, and Cookie global arrays.
     */
    function reset_gpc()
    {
        foreach ($_GET as $key=>$value)
        {
            unset($_GET[$key]);
        }

        foreach ($_POST as $key=>$value)
        {
            unset($_POST[$key]);
        }

        foreach ($_COOKIE as $key=>$value)
        {
            unset($_COOKIE[$key]);
        }
    }
    /**
     * Sets error handling mode for the page.
     *
     * @param int $mode
     */
    function set_error_handling($mode)
    {
        // E_STRICT will be part of PHP 6, so might as well start now
        error_reporting(E_ALL | E_STRICT);
    
        // Make things exceptions instead of errors (when possible)
        set_error_handler('n\z\error_handler');
    
        if ($mode == 'dev' || $mode == 'test')
        {
            ini_set('display_errors', '1');
            set_exception_handler('n\z\dev_handler');
        }
        else
        {
            ini_set('display_errors', '0');
            set_exception_handler('n\z\prod_handler');
        }
    }
    /**
     * Handles errors by throwing them as exceptions with the underlying info contained in the message.
     *
     * @param int $errno
     * @param string $errstr
     * @param string $errfile
     * @param int $errline
     */
    function error_handler($errno, $errstr, $errfile, $errline)
    {
        // must take into account error suppressor (@) and not do anything with them (they equal 0)
        // http://framework.zend.com/issues/browse/ZF-3829
        if ($errno < 1)
        {
            $error_msg = "<dl><dt>Error Message:</dt><dd>$errstr</dd><dt>File:</dt><dd>$errfile</dd><dt>Line:</dt><dd>$errline</dd></dl>";
            throw new \Exception($error_msg);
        }
    }
    /**
     * Handles exceptions not caught by the page when the application is in prod mode.
     * Any output is erased and the user is redirected to the default error page set in nconfig.php.
     * @param void $exception
     */
    function prod_handler($exception)
    {
        \n\url\redirect(\n\config\get('error_page'));
    }
    /**
     * Handles exceptions not caught by the page when the application is in dev mode.
     * Details of the exception and a backtrace of the code that led to the exception are output to the screen.
     * @param void $exception
     */
    function dev_handler($exception)
    {
        // Get rid of any output that's already buffered
        ob_end_clean();
    
        ?>
        <html>
        <head>
        <title>Debug output for unhandled exception</title>
        </head>
        <body>
        <h1>Debug output for unhandled exception</h1>
        <?php echo $exception->getMessage(); ?><br />
        <h2>Debug Backtrace</h2>
        <pre><?php echo \n\str\encode(print_r(debug_backtrace(), true)); ?></pre>
        </body>
        </html>
        <?php
        die();
    }
    function value($domain, $command, array $inputs, $index = 'name')
    {
        static $values = array();

        if (!isset($values[$domain])) $values[$domain] = array();

        if ($command == 'isset')
        {
            $results = array();

            foreach ($inputs as $key)
            {
                $results[$key] = isset($values[$domain][$key]);
            }

            return $results;
        }

        if ($command == 'get')
        {
            if (count($inputs))
            {
                return array_intersect_key($values[$domain], array_fill_keys($inputs, 0));
            }
            else
            {
                return $values[$domain];
            }
        }

        if ($command == 'set')
        {
            if (!isset($values[$domain][$inputs[$index]]))
            {
                $values[$domain][$inputs[$index]] = $inputs;
                return;
            }
            else
            {
                throw new \Exception('There is already a '.$values[$domain].' called "'.$inputs[$index].'" that has been registered for this page.');
            }
        }

        throw new \Exception($message = 'The n\z\value() function did not receive one of the valid commands: isset, get, or set.');
    }
}
namespace n\z\pipe
{
    function process_registered($markup)
    {
        /**
         * Get configuration set up.
         */
        require('nconfig.php');
        /**
         * Configure environment with config settings.
         */
        \n\z\set_error_handling(\n\config\get('mode'));
        /**
         * If it's production or development mode, and if nmode isn't set, handle post requests.
         */
        if ((\n\config\get($names = 'mode') == 'dev' || \n\config\get($names = 'mode') == 'prod') && !isset($_GET['nmode'])) \n\z\post\process_request();
        /**
         * Register pipes in npipe.php (authentication, etc.)
         */
        require('nregister.php');
        /**
         * Register code-behind pipes and ports, if present
         */
        if (\n\z\file\exists('nsite'.$_SERVER['PHP_SELF']))
        {
            require('nsite'.$_SERVER['PHP_SELF']);
        }

        $nmode_whitelist = array('htmlfrag','validate','debug');

        \n\port\register($name = 'nmode', $value = $_GET['nmode'], $opts = array('whitelist' => $nmode_whitelist));
        \n\port\register($name = 'npipe', $value = $_GET['npipe'], $opts = array('max_length' => 40, 'filter' => \n\constant\FILTER_VAR_NAME));
        \n\port\register($name = 'nports', $value = $_GET['nports'], $opts = array('max_length' => 700, 'filter' => \n\constant\FILTER_CUSTOM, 'regex' => '/^[-,a-zA-Z0-9_]+$/'));

        // get rid of the GPC vars so they can't be used outside of ports
        unset($_GET);
        unset($_POST);
        unset($_COOKIE);

        if (\n\port\signature($ports = array('nmode')))
        {
            if (count($errors = \n\port\validate(array('nmode')))) throw new \Exception('The nmode get variable we received was not valid.');

            if ((\n\port\get('nmode') == 'validate') && \n\port\signature($ports = array('nports')))
            {
                return render_json_request();
            }

            if (\n\port\get('nmode') == 'debug')
            {
                return render_debug_request($markup);
            }

            if ((\n\port\get('nmode') == 'htmlfrag') && \n\port\signature($ports = array('npipe')))
            {
                return render_values(render_htmlfrag_request($markup));
            }
        }
        // otherwise, process standard request
        else
        {
            return render_standard_request($markup);
        }
    }
    function render_values($markup)
    {
        $row = \n\z\value($domain = 'output', $command = 'get', $inputs = array());
        $new_row = array();

        foreach ($row as $name => $arr)
        {
            $new_row[$name] = $arr['value'];
        }
        
        return \n\row\databind($markup, $new_row, $opts = array());
    }
    function render_json_request()
    {
        // return a JSON array containing validation info
        if(count(\n\port\validate($ports = array('nports'))))
        {
            throw new \Exception('The nports get variable was not valid for this request.');
        }

        $port_names = explode(',', \n\port\get('nports'));

        if (!$rows = \n\port\validate($port_names))
        {
            return '[]';
        }
        else
        {
            $invalid_values = '[';

            foreach($rows as $name => $row)
            {
                $invalid_values .= '["'.$name.'","'.str_replace('"', '\"', $row['message']).'"]';
            }

            return $invalid_values.']';
        }
    }
    function render_debug_request()
    {
        require('debug.html');

        $markup = ob_get_clean();

        if (!isset($_SESSION)) session_start();

        $requests = array_reverse($_SESSION['nephtali_debug']);

        $sections = \n\markup\get_sections($markup, $type = 'pipe', $name = 'debug');

        $sections[1] = '';

        foreach($requests as $request)
        {
            $sections[1] .= <<<EOD
            <div>
                <h2>{$request['page_requested']} <span class="timestamp">{$request['datetime']}</span></h2>
EOD;
            if (isset($request['vars'])) $sections[1] .= display_watch_info($request['vars']);
            if (isset($request['ports'])) $sections[1] .= display_port_info($request['ports']);
            if (isset($request['gpcs'])) $sections[1] .= display_gpcs_info($request['gpcs']);

            $sections[1] .= '</div>';
        }

        return implode('', $sections);
    }
    function render_htmlfrag_request($markup)
    {
        if (count(\n\port\validate($ports = array('npipe'))))
            throw new \Exception('The npipe get variable was not valid.');

        if (\n\config\get($names = 'mode') == 'dev') store_debug_values();

        $pipe_name = \n\port\get('npipe');

        $pipe = \n\pipe\get($pipe_name);

        if (!$pipe['opts']['has_output'])
            throw new \Exception('The nmode "htmlfrag" cannot be used on a pipe that has "has_output" set to false.');

        // render any required pipes first
        foreach ($pipe['opts']['required_pipes'] as $rp_name)
        {
            $rp = \n\pipe\get($rp_name);

            $markup_sections = \n\markup\get_sections($markup, $type = 'pipe', $name = $rp_name);
            $rendering_function = $rp['function'];

            try
            {
                $markup_sections[1] = $rendering_function($markup_sections[1]);
                $markup = implode('', $markup_sections);
            }
            catch (\Exception $e)
            {
                if (!$rp['opts']['auto_error_handling']) throw $e;

                $markup_sections[1] = \n\view\render($view = 'error', $markup_sections[1]);
                $markup = implode('', $markup_sections);
            }
        }

        // now render the requested pipe
        $markup_sections = \n\markup\get_sections($markup, $type = 'pipe', $name = $pipe_name);
        $rendering_function = $pipe['function'];

        try
        {
            return $rendering_function($markup_sections[1]);
        }
        catch (\Exception $e)
        {
            if (!$pipe['opts']['auto_error_handling']) throw $e;

            return \n\view\render($view = 'error', $markup_sections[1]);
        }
    }
    function render_standard_request($markup)
    {   
        if (\n\config\get($names = 'mode') == 'dev') store_debug_values();

        // on a standard request, preprocess all curl requests in parallel
        $responses = \n\curl\get_contents_parallel(\n\z\value($domain = 'request', $command = 'get', $inputs = array()));

        foreach ($responses as $url => $output)
        {
            \n\z\value($domain = 'response', $command = 'set', $inputs = array('url' => $url, 'output' => $output), $index = 'url');
        }

        $page_pipes = \n\pipe\get();

        foreach($page_pipes as $name => $pipe)
        {
            // These pipes don't impact output and don't require any pipe comments in the markup of the page (e.g., authorization, etc.)
            if (!$pipe['opts']['has_output'])
            {
                $function = $pipe['function'];
                $function();
            }
            // Standard pipes that impact output and require pipe comments in the markup to show where the results should be placed.
            else
            {
                $markup_sections = \n\markup\get_sections($markup, $type = 'pipe', $name);
                $rendering_function = $pipe['function'];

                try
                {
                    $markup_sections[1] = $rendering_function($markup_sections[1]);
                }
                catch (\Exception $e)
                {
                    if (!$pipe['opts']['auto_error_handling']) throw $e;
                
                    $markup_sections[1] = \n\view\render($view = 'error', $markup_sections[1]);
                }
                
                $markup = implode('', $markup_sections);
            }
        }

        return render_values($markup);
    }
    function store_debug_values()
    {
        if (!isset($_SESSION)) session_start();
        if (!isset($_SESSION['nephtali_debug']))
        {
            $_SESSION['nephtali_debug'] = array();
        }

        $values['datetime'] = date(DATE_RFC822);
        $values['page_requested'] = $_SERVER['PHP_SELF'];

        // first, store gpcs data
        foreach ($_GET as $key=>$value)
        {
            if ($key != 'nmode' && $key != 'npipe' && $key != 'nports') $values['gpcs']['get'][$key] = $value;
        }
        foreach ($_POST as $key=>$value)
        {
            $values['gpcs']['post'][$key] = $value;
        }
        foreach ($_COOKIE as $key=>$value)
        {
            $values['gpcs']['cookie'][$key] = $value;
        }
        foreach ($_SESSION as $key=>$value)
        {
            if ($key != 'nephtali_debug') $values['gpcs']['session'][$key] = $value;
        }

        // second, store port data
        $ports = \n\z\value($domain = 'port', $command = 'get', $inputs = array());

        foreach ($ports as $name => $value)
        {
            if ($name == 'nmode' || $name == 'npipe' || $name == 'nports') continue;
            $values['ports'][$name] = array('name' => $name, 'valid' => $value['valid'], 'value' => $value['value']);
        }

        // add a new values item on the the end of the array
        $_SESSION['nephtali_debug'][] = $values;

        while (count($_SESSION['nephtali_debug']) > 10)
        {
            // delete the first item in array
            array_shift($_SESSION['nephtali_debug']);
        }
    }
    function display_gpcs_info($gpcs)
    {
        $markup = '';

        foreach ($gpcs as $key => $value)
        {
            $markup .= '<h3>'.$key.'</h3><dl>';
            foreach ($gpcs[$key] as $name => $value)
            {
                $markup .= '<dt>'.$name.'</dt>';
                $markup .= '<dd>'.htmlspecialchars($value, ENT_QUOTES, $charset).'</dd>';
            }
            $markup .= '</dl>';
        }
        return $markup;
    }
    function display_port_info($ports)
    {
        $markup = <<<EOD
<h3>Ports</h3>
<table class="ports">
    <thead>
        <tr>
            <th>Port name</th>
            <th>Port value</th>
            <th>Valid</th>
        </tr>
    </thead>
    <tbody>
EOD;

        foreach ($ports as $values)
        {
            $markup .= '<tr '.(($values['valid'])?'':'class="invalid"').'><td>'.$values['name'].'</td><td>'.htmlspecialchars($values['value'], ENT_QUOTES, $charset).'</td><td>'.(($values['valid'])?'valid':'invalid').'</td></tr>';
        }

        $markup .= '</tbody></table>';

        return $markup;
    }
    function display_watch_info($vars)
    {
        if (!count($vars)) return '';

        $markup = '<h3>Watched Variables</h3><dl>';

        foreach ($vars as $var)
        {
            $markup .= '<dt>'.$var['name'].'</dt><dd>'.print_r($var['value'], true).'</dd>';
        }

        $markup .= '</dl>';

        return $markup;
    }
}
namespace n\z\port
{
    function save($arr)
    {
        if ($arr['type'] == 'char_data')
        {
            // used for error output
            $formatted_name = str_replace('_', ' ', ucfirst($arr['name']));

            $defaults = array(
                'min_length'         => 1,
                'max_length'         => 10,
                'min_value'          => 0,
                'max_value'          => 9999999999,
                'filter'             => \n\constant\FILTER_INT,
                'regex'              => null,
                'encrypted'          => false,
                'allow_null'         => false,
                'whitelist'          => array(),
                'allow_array'        => false,
                'formatted_name'     => $formatted_name,
                'error_messages'     => array(),
                'error_message'      => null,
                'validators'         => array(),
                'formatter'          => null
            );

            $opts = array_merge($defaults, $arr['opts']);

            $whitelist = "'".$opts['formatted_name']."' only accepts the following values:  ".implode(',', $opts['whitelist']).".";

            switch ($opts['filter'])
            {
                case \n\constant\FILTER_INT:
                    $error_message = "'{$opts['formatted_name']}' only accepts numbers.";
                    break;
                case \n\constant\FILTER_DOUBLE:
                    $error_message = "'{$opts['formatted_name']}' only accepts positive or negative numbers with or without decemals.";
                    break;
                case \n\constant\FILTER_ALPHANUM:
                    $error_message = "'{$opts['formatted_name']}' only accepts numbers and letters as input.";
                    break;
                case \n\constant\FILTER_VAR_NAME:
                    $error_message = "'{$opts['formatted_name']}' only accepts numbers, letters, underscores, and dashes as input.";
                    break;
                case \n\constant\FILTER_US_PHONE:
                    $error_message = "'{$opts['formatted_name']}' only accepts valid US phone numbers as input.";
                    break;
                case \n\constant\FILTER_US_ZIP:
                    $error_message = "'{$opts['formatted_name']}' only accepts valid US zip codes as input.";
                    break;
                case \n\constant\FILTER_URL:
                    $error_message = "'{$opts['formatted_name']}' only accepts valid URLs as input.";
                    break;
                case \n\constant\FILTER_EMAIL:
                    $error_message = "'{$opts['formatted_name']}' only accepts valid email addresses as input.";
                    break;
                case \n\constant\FILTER_TEXT:
                    $error_message = "'{$opts['formatted_name']}' only accepts a single line of basic text as input (no HTML input allowed.)";
                    break;
                case \n\constant\FILTER_TEXT_MULTILINE:
                    $error_message = "'{$opts['formatted_name']}' only accepts basic text as input (no HTML input allowed.)";
                    break;
                case \n\constant\FILTER_XML:
                    $error_message = "'{$opts['formatted_name']}' only accepts text and XML as input.";
                    break;
                case \n\constant\FILTER_CUSTOM:
                    $error_message = "'{$opts['formatted_name']}' is not valid data.";
                default:
                    $error_message = "The data received was invalid.";
            }

            $default_messages = array(
                'min_length'         => "'{$opts['formatted_name']}' has a minimum length of  {$opts['min_length']} character(s).",
                'max_length'         => "'{$opts['formatted_name']}' has a maximum length of  {$opts['max_length']} character(s).",
                'min_value'          => "'{$opts['formatted_name']}' has a minimum value of  {$opts['min_value']}.",
                'max_value'          => "'{$opts['formatted_name']}' has a maximum value of  {$opts['max_value']}.",
                'allow_null'         => "'{$opts['formatted_name']}' is an expected value, and we did not recieve it.",
                'whitelist'          => $whitelist,
                'allow_array'        => "'{$opts['formatted_name']}' does not allow arrays of data.",
                'filter'             => $error_message
            );

            if ($opts['min_length'] == 1) $default_messages['min_length'] = "'{$opts['formatted_name']}' is required and cannot be left blank.";

            $opts['error_messages'] = array_merge($default_messages, $opts['error_messages']);

            if ($opts['encrypted'] && !is_null($arr['value']))
            {
                $arr['value'] = \n\security\decrypt($arr['value']);
            }

            $formatter = $opts['formatter'];
            
            $arr = validate_chardata(array('name' => $arr['name'], 'formatted_name' => $formatted_name, 'value' => ((is_null($formatter))?$arr['value']:$formatter($arr['value'])), 'opts' => $opts));
            \n\z\value($domain = 'port', $command = 'set', $inputs = $arr);
            return;
        }
        elseif ($arr['type'] == 'file')
        {
            // used for error output
            $formatted_name = str_replace('_', ' ', ucfirst($arr['name']));

            $defaults = array(
                'max_bytes'          => 1048576,
                'required'           => true,
                'extensions'         => array('jpg', 'jpeg', 'gif'),
                'ftp_username'       => null,
                'ftp_password'       => null,
                'ftp_domain'         => null,
                'formatted_name'     => $formatted_name
            );

            $opts = array_merge($defaults, $arr['opts']);

            $friendly_bytes = \n\z\bytes\get_friendly($opts['min_length']);
            $extensions = "'".$opts['formatted_name']."' only accepts files with the following extensions:  ".implode(',', $opts['extensions']).".";

            $default_messages = array(
                'max_bytes'          => "'{$opts['formatted_name']}' has a maximum size of $freindly_bytes.",
                'required'           => "'{$opts['formatted_name']}' is required.",
                'extensions'         => $extensions,
                'is_upload'          => "'{$opts['formatted_name']}' does not appear to be an uploaded file."
            );

            $opts['error_messages'] = array_merge($default_messages, $opts['error_messages']);

            $arr = validate_file(array('name' => $arr['name'], 'formatted_name' => $formatted_name, 'value' => $arr['value'], 'opts' => $opts));
            \n\z\value($domain ='port', $command = 'set', $inputs = $arr);
            return;
        }
        else
        {
            throw new \Exception('The type for the port was not recognized.');
        }
    }
    function validate_file(array $arr)
    {
        if ($arr['value']['size'] < 1)
        {
            if ($arr['opts']['required']) return declare_invalid($arr, $failed_test = 'required');

            return declare_valid($arr);
        }

        if (!is_uploaded_file($arr['value']['tmp_name']))
        {
            return declare_invalid($arr, $failed_test = 'is_upload');
        }

        if ($arr['value']['size'] > $arr['opts']['max_bytes'])
        {
            return declare_invalid($arr, $failed_test = 'max_bytes');
        }

        $info = pathinfo($arr['value']['name']);

        if (!in_array($info['extension'], $arr['opts']['extensions']))
        {
            return declare_invalid($arr, $failed_test = 'extensions');
        }

        return declare_valid($arr);
    }
    function validate_chardata(array $arr)
    {   
        // check for NULLs
        if (is_null($arr['value']))
        {
            if ($arr['opts']['allow_null'])
            {
                return declare_valid($arr);
            }
            else
            {
                return declare_invalid($arr, $failed_test = 'allow_null');
            }
        }
        
        // check for arrays
        if (is_array($arr['value']))
        {
            if (!$arr['opts']['allow_array']) return declare_invalid($arr, $failed_test = 'allow_array');

            $values = $arr['value'];

            foreach ($values as $val)
            {
                // make copy of array
                $temp_arr = $arr;
                // replace value with singular value of this iteration
                $temp_arr['value'] = $val;
                // validate
                $temp_arr = validate_chardata_value($temp_arr);
                if (!$temp_arr['valid'])
                {
                    $temp_arr['values'] = $values;
                    return $temp_arr;
                }
            }

            return declare_valid($arr);
        }
        else
        {
            return validate_chardata_value($arr);
        }
    }
    function validate_chardata_value($arr)
    {
        // whitelist validation
        if(count($arr['opts']['whitelist']) > 0)
        {
            if (!in_array($arr['value'], $arr['opts']['whitelist']))
            {
                return declare_invalid($arr, $failed_test = 'whitelist');
            }

            return declare_valid($arr);
        }
        // if value is '' and minLength is 0, don't wast processing
        if ($arr['value'] == '' && $arr['opts']['min_length'] == 0)
        {
            return declare_valid($arr);
        }
        // check max length
        if(strlen($arr['value']) > $arr['opts']['max_length'])
        {
            return declare_invalid($arr, $failed_test = 'max_length');
        }
        // check min length
        if(strlen($arr['value']) < $arr['opts']['min_length'])
        {
            return declare_invalid($arr, $failed_test = 'min_length');
        }
        // filter
        switch ($arr['opts']['filter'])
        {
            case \n\constant\FILTER_INT:
                if (!preg_match(\n\constant\REGEX_INT, $arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                // also check value if an int
                if (!($arr['value'] >= $arr['opts']['min_value']))
                    return declare_invalid($arr, $failed_test = 'min_value');
                if (!($arr['value'] <= $arr['opts']['max_value']))
                    return declare_invalid($arr, $failed_test = 'max_value');
                break;
            case \n\constant\FILTER_DOUBLE:
                if (!preg_match(\n\constant\REGEX_DOUBLE, $arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                // also check value if a double
                if (!($arr['value'] >= $arr['opts']['min_value']))
                    return declare_invalid($arr, $failed_test = 'min_value');
                if (!($arr['value'] <= $arr['opts']['max_value']))
                    return declare_invalid($arr, $failed_test = 'max_value');
                break;
            case \n\constant\FILTER_ALPHANUM:
                if (!preg_match(\n\constant\REGEX_ALPHANUM, $arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            case \n\constant\FILTER_VAR_NAME:
                if (!preg_match(\n\constant\REGEX_VAR_NAME, $arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            case \n\constant\FILTER_US_PHONE:
                if (!preg_match(\n\constant\REGEX_US_PHONE, $arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            case \n\constant\FILTER_US_ZIP:
                if (!preg_match(\n\constant\REGEX_US_ZIP, $arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            case \n\constant\FILTER_URL:
                if (!preg_match(\n\constant\REGEX_URL, $arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            case \n\constant\FILTER_EMAIL:
                if (!preg_match(\n\constant\REGEX_EMAIL, $arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            case \n\constant\FILTER_TEXT:
                if (!preg_match(\n\constant\REGEX_TEXT, $arr['value']) || contains_xss($arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            case \n\constant\FILTER_TEXT_MULTILINE:
                if (!preg_match(\n\constant\REGEX_TEXT_MULTILINE, $arr['value']) || contains_xss($arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            case \n\constant\FILTER_XML:
                if (!preg_match(\n\constant\REGEX_XML, $arr['value']) || contains_xss($arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            case \n\constant\FILTER_ALL:
                break;
            case \n\constant\FILTER_CUSTOM:
                if (!preg_match($arr['opts']['regex'], $arr['value']))
                    return declare_invalid($arr, $failed_test = 'filter');
                break;
            default:
                throw new \Exception('The filter supplied for the port "'.$arr['name'].'" is not valid.');
        }

        foreach ($arr['opts']['validators'] as $error_message => $func)
        {
            if (!$func($arr['value'])) return declare_invalid($arr, $failed_test = $name, $error_message);
        }

        return declare_valid($arr);
    }
    function declare_valid(array $arr)
    {
        $arr['valid'] = true;
        $arr['signature'] = true;
        $arr['error_message'] = null;
        return $arr;
    }
    function declare_invalid(array $arr, $failed_test, $error_message = null)
    {
        if (is_null($error_message))
        {
            $arr['error_message'] = $arr['opts']['error_messages'][$failed_test];
        }
        else
        {
            $arr['error_message'] = \str_replace($search = '{formatted_name}', $replace = $arr['formatted_name'], $subject = $error_message);
        }

        $arr['valid'] = false;
        ($failed_test == 'allow_null')?($arr['signature'] = false):($arr['signature'] = true);
        return $arr;
    }
    function contains_xss($value)
    {
        $xss_hackemes = array(
            '%3c', // <
            '&lt', // <
            'pa==',// <
            '&gt', // >
            '%3E', // >
            'pg==',// >
            '&quo',// "
            '%22', // "
            'ig==',// "
            '#27', // '
            'jw==',// '
            '&#',  // generally not needed
            '\x',  // generally not needed
            '\u',  // generally not needed
            'javascript',
            'onsubmit',
            'onabort',
            'onblur',
            'onchange',
            'onfocus',
            'onmouse',
            'onload',
            'onreset',
            'onselect',
            'onunload',
            'onerror',
            'onclick',
            '<script'
        );

        $lc_value = strtolower($value);

        foreach($xss_hackemes as $hackeme)
        {
            if(strpos($lc_value, $hackeme)===false)
            {
                continue;
            }
            else
            {
                return true;
            }
        }

        return false;
    }
}
namespace n\z\bytes
{
    function compute($val)
    {
        $val = trim($val);

        switch ($last = strtolower(substr($val, -1))) {
            // The 'G' modifier is available since PHP 5.1.0
            case 'g':
                return substr($val, 0, strlen($val) - 1)*1073741824;
            case 'm':
                return substr($val, 0, strlen($val) - 1)*1048576;
            case 'k':
                return substr($val, 0, strlen($val) - 1)*1024;
            default:
                return $val;
        }
    }
    function get_friendly($val)
    {
        if ($val < 1024)
        {
            return $val.' bytes';
        }
        if ($val < 1048576)
        {
            return ($val/1024).'KB';
        }
        if ($val < 1073741824)
        {
            return ($val/1048576).'MB';
        }

        return ($val/1073741824).'GB';
    }
}
namespace
{
    $markup = ob_get_contents();
    ob_clean();
    echo n\z\pipe\process_registered($markup);
}

?>